package tinybase.rm;

import tinybase.basic.BytePointer;
import tinybase.basic.RedBase;
import tinybase.basic.RedBase.AttrType;
import tinybase.basic.RedBase.ClientHint;
import tinybase.basic.RedBase.CompOp;
import tinybase.basic.RC;
import tinybase.pf.PF_FileHandle;
import tinybase.pf.PF_PageHandle;
import tinybase.pf.PageNum;

public class RM_FileScan {


	private boolean bScanOpen;
	private int curPageNum;
	private int curSlotNum;
	private RM_FileHandle pFileHandle;
	private AttrType attrType;
	private int attrLength;
	private int attrOffset;
	private CompOp compOp;
	private Object value;
	private ClientHint pinHint;
	private PF_PageHandle pageHandle;

	// 
	// RM_FileScan
	//
	// Desc: Default Constructor
	//
	public RM_FileScan() {
		// TODO Auto-generated constructor stub

		// Initialize member variables
		bScanOpen = false;
		curPageNum = RedBase.RM_HEADER_PAGE_NUM;
		curSlotNum = 0;

		pFileHandle = null;
		attrType = AttrType.INT;
		attrLength = 4;
		attrOffset = 0;
		compOp = CompOp.NO_OP;
		value = null;
		pinHint = ClientHint.NO_HINT;
	}



	//
	// OpenScan
	//
	// Desc: Open a file scan with the given fileHandle and scan condition
	// In:   fileHandle  - RM_FileHandle object (must be open)
	//           _attrType   - INT|FLOAT|STRING
	//           _attrLength - 4 for INT|FLOAT, 1~MAXSTRING for STRING
	//           _attrOffset - indicates the location of the attribute
	//           _compOp     - EQ_OP|LT_OP|GT_OP|LE_OP|GE_OP|NE_OP|NO_OP
	//           _value      - points to the value which will be compared with
	//                         the given attribute
	//           _pinHint    - not implemented yet
	// Ret:  RM_SCANOPEN, RM_VALUENULL, RM_INVALIDATTR, RM_CLOSEDFILE
	//
	RC openScan(RM_FileHandle fileHandle, AttrType _attrType,
			int _attrLength, int _attrOffset, CompOp _comOp,
			Value _value, ClientHint _pinHint) {

		// Sanity Check: 'this' should not be open yet
		if (bScanOpen)
			// Test: opened RM_FileScan
			return (RC.RM_SCANOPEN);

		// Sanity Check: fileHandle must be open
		if (fileHandle.fileHdr.getRecordSize() == 0) // a little tricky here
			// Test: unopened fileHandle
			return (RC.RM_CLOSEDFILE);

		// Sanity Check: compOp
		switch (_comOp) {
		case EQ_OP:
		case LT_OP:
		case GT_OP:
		case LE_OP:
		case GE_OP:
		case NE_OP:
		case NO_OP:
			break;

		default:
			return (RC.RM_INVALIDCOMPOP);
		}

		if (_comOp != RedBase.CompOp.NO_OP) {
			// Sanity Check: value must not be NULL
			if (_value == null)
				// Test: null _value
				return (RC.RM_NULLPOINTER);

			// Sanity Check: attrType, attrLength
			switch (_attrType) {
			case INT:
			case FLOAT:
				if (_attrLength != 4)
					// Test: wrong _attrLength
					return (RC.RM_INVALIDATTR);
				break;

			case STRING:
				if (_attrLength < 1 || _attrLength > RedBase.MAXSTRINGLEN)
					// Test: wrong _attrLength
					return (RC.RM_INVALIDATTR);
				break;

			default:
				// Test: wrong _attrType
				return (RC.RM_INVALIDATTR);
			}

			// Sanity Check: attrOffset
			if (_attrOffset < 0 
					|| _attrOffset + _attrLength > fileHandle.fileHdr.getRecordSize())
				// Test: wrong _attrOffset/_attrLength
				return (RC.RM_INVALIDATTR);
		}

		// Copy parameters to local variable
		//pFileHandle = (RM_FileHandle *)&fileHandle;
		RM_FileHandle tmpFh = new RM_FileHandle();
		tmpFh = (RM_FileHandle)fileHandle;
		pFileHandle = new RM_FileHandle(tmpFh); //--------  Need copy constructor to be define

		attrType    = _attrType;
		attrLength  = _attrLength;
		attrOffset  = _attrOffset;
		compOp      = _comOp;
		value       =  _value;
		pinHint     = _pinHint;

		// Set local state variables
		bScanOpen = true;
		curPageNum = RedBase.RM_HEADER_PAGE_NUM;
		curSlotNum = pFileHandle.fileHdr.getNumRecordsPerPage();

		return RC.RM_SUCCESS;
	}

	//
	// GetNextRec
	//
	// Desc: Retrieve a copy of the next record that satisfies the scan condition
	// Out:  rec - Set to the next matching record
	// Ret:  RM or PF return code
	//
	RC getNextRec(RM_Record rec) {

		RC rc;
		BytePointer pData = new BytePointer();
		RID curRid = new RID(); //Pointer

		// Sanity Check: 'this' must be open
		if (!bScanOpen)
			return (RC.RM_CLOSEDSCAN);

		// Sanity Check: fileHandle must be open
		if (pFileHandle.fileHdr.getRecordSize() == 0) // a little tricky here
			// Test: unopened fileHandle
			return (RC.RM_CLOSEDFILE);

		// Fetch another page if required
		if (curSlotNum == pFileHandle.fileHdr.getNumRecordsPerPage()) {
			repeat:
				// Get next page
				if ((rc=pFileHandle.pfFileHandle.getNextPage(curPageNum, pageHandle)) == RC.PF_SUCCESS)
					return rc;

		// Update curPageNum
		if ((rc = pageHandle.getPageNum(new PageNum(curPageNum))) != RC.RM_SUCCESS)
			// Should not happen
			// In fact, we need to unpin the page, but don't know the page number
			return rc;

		// Reset curSlotNum
		curSlotNum = 0;
		}
		// We didn't process the whole page in the previous GetNextRec() call
		else {
			if ((rc = pFileHandle.pfFileHandle.getThisPage(curPageNum, pageHandle)) != RC.RM_SUCCESS)
				if (rc == RC.PF_INVALIDPAGE)
					// We can get PF_INVALIDPAGE if curPageNum was disposed
					return getNextRec(rec); // Recursivity to verify goto repeat;
				else
					// Test: closed fileHandle
					return rc;
		}

		//
		if ((rc = pageHandle.getData(pData)) != RC.RM_SUCCESS)
			// Should not happen
			//Unpin the page;
			if ((rc = pFileHandle.pfFileHandle.unpinPage(curPageNum)) != RC.RM_SUCCESS)
				// Should not happen
				return rc;

		// Find the next record based on scan condition
		findNextRecInCurPage(pData.getArray());

		// No HIT in this page, go to next page
		if (curSlotNum == pFileHandle.fileHdr.getNumRecordsPerPage()) {
			// Unpin this page
			if ((rc = pFileHandle.pfFileHandle.unpinPage(curPageNum)) != RC.RM_SUCCESS)
				// Should not happen
				return rc;

			return getNextRec(rec); // Recursivity to verify goto repeat;
		}

		// HIT: copy the record to the given location

		rec.setRid(new RID(curPageNum, curSlotNum));  // ------------  Need method setRid  -------------
		

		if (rec.getpData() != null)  //---------------  Need getter and setter
			rec.setPdata(null);
		rec.setRecordSize(pFileHandle.fileHdr.getRecordSize());  //  --------------  Need setter
		
		BytePointer tpData  = new BytePointer(new byte[rec.getRecordSize()]);
		tpData.setArray(new byte[pData.getArray().length+ 
		                pFileHandle.fileHdr.getPageHeaderSize()+
		                curSlotNum*pFileHandle.fileHdr.getRecordSize()
		                ]);
		rec.setPdata(tpData.getArray());
		
		// Increment curSlotNum
		curSlotNum++;

		// If there is no more matching record in this page,
		// we don't have to access this page in the next GetNextRec() call.
		findNextRecInCurPage(pData.getArray());

		// Unpin this page
		if ((rc = pFileHandle.pfFileHandle.unpinPage(curPageNum)) != RC.RM_SUCCESS)
			// Should not happen
			return rc;

		// Return ok
		return RC.RM_SUCCESS;

	}

	
	//
	// FineNextRecInCurPage
	//
	// Desc: Iterates slots in the current page (until hit or end)
	// In:   pData - points a data page buffer
	//
	RC findNextRecInCurPage(byte[] addr){
		
	   for ( ; curSlotNum < pFileHandle.fileHdr.getNumRecordsPerPage(); curSlotNum++) {
	      float cmp;
	      int i;
	      float f;

	      /////TODO -----
	      // Skip empty slots
	      int sizeOPageHdr = 8;  //-------------  Find the cons location
	      byte[] tpData;
	      if ((pFileHandle.getBitmap(tpData, curSlotNum)) != RC.RM_SUCCESS)  //---------------  getBitmap() need to be visible
	         continue;
	      
	      // Hit if NO_OP
	      if (compOp == CompOp.NO_OP)
	         break;

	      // Do comparison according to the attribute type
	      switch (attrType) {
	      case INT:
	    	  i = addr.length+ 
              pFileHandle.fileHdr.getPageHeaderSize()+
              curSlotNum*pFileHandle.fileHdr.getRecordSize()+
              attrOffset;
	         cmp = (new byte[i]).equals(new byte[value.]);  // Compare 2 adresses
	         break;

	      case FLOAT:
	         memcpy(&f,
	                pData + pFileHandle->fileHdr.pageHeaderSize
	                + curSlotNum * pFileHandle->fileHdr.recordSize 
	                + attrOffset,
	                sizeof(float));
	         cmp = f - *((float *)value);
	         break;

	      case STRING:
	         cmp = memcmp(pData + pFileHandle->fileHdr.pageHeaderSize
	                      + curSlotNum * pFileHandle->fileHdr.recordSize 
	                      + attrOffset,
	                      value,
	                      attrLength);
	         break;
	      }

	      // Make decision according to comparison operator
	      if ((compOp == EQ_OP && cmp == 0)
	          || (compOp == LT_OP && cmp < 0)
	          || (compOp == GT_OP && cmp > 0)
	          || (compOp == LE_OP && cmp <= 0)
	          || (compOp == GE_OP && cmp >= 0)
	          || (compOp == NE_OP && cmp != 0))
	         break;
	   }
	}
	
	
		return RC.RM_SUCCESS;
	}

	RC closeScan() {
		return RC.RM_SUCCESS;
	}

}
